function context
JavaScriptには関数の呼び方が5つあって、それぞれthisの値が異なると言う話
code:javascript
// 関数として呼び出し
function f() {}
f()
// メソッドとして呼び出し
const obj = {}
obj.f = function() {}
obj.f()
// コンストラクタとして呼び出し
function Obj2() = {
this.f = function() {}
}
const obj2 = new Obj2()
obj2.f()
// apply, callを用いて呼び出し
const obj3 = {}
const obj4 = {}
obj3.apply(f, 0, 1, 2)
obj4.call(f, 0, 1, 2)
// arrow functionとして呼び出し
const fun = () => {}
fun()
関数として呼び出し
code:javascript
function f() {
return this
}
function f2() {
'use strict'
return this
}
console.log(f() === window) // true
console.log(f2() === undefined) // true
関数として呼び出した場合、function contextはグローバルオブジェクト(window)
strict modeの場合はundefinedとなる。
メソッドとして呼び出し
code:javascript
function getThis() {
return this
}
const obj = {
f: getThis
}
console.log(getThis() === window) // true
console.log(obj.f() === obj) // true
メソッドとして呼び出した場合、(それがグローバルに定義された関数の参照であったとしても)thisの値はメソッドが定義されているオブジェクトそのものとなる。
言い換えれば、function contextが何を返すかは「どこに関数が定義されているか?」ではなく、「関数がどう呼び出されたか?」に依存している。
コンストラクタとして呼び出し
code:javascript
function Obj() {
this.getThis = function() {
return this
}
}
const obj1 = new Obj()
const obj2 = new Obj()
console.log(obj1.getThis() === obj1) // true
console.log(obj2.getThis() === obj2) // true
コンストラクタとして呼び出した後の新しいオブジェクトのメソッドのfunction contextは、それぞれのオブジェクトとなる。
コンストラクタを呼び出した際の動作
空のオブジェクトが作成される(a)
(a)がfunction contextとしてセットされる
プロパティが(a)に設定される
(a)が結果として返却される
オブジェクトを返すコンストラクタの注意点
code:javascript
const a = {
property: 123
}
function Obj() {
this.property = 456
return a
}
const obj = new Obj()
console.log(obj === a) // true
console.log(obj.property === 123) // true
コンストラクタとして利用した関数がオブジェクトを返却する場合、上記の「コンストラクタを呼び出した際の動作」は無視され、関数が返却するオブジェクトが結果として返却される。
code:javascript
function Obj() {
this.property = 456
return 1
}
const obj = new Obj()
console.log(obj !== 1) // true
console.log(obj.property === 456) // true
オブジェクトでなく値が返却される場合は、コンストラクタとして正常に動作する(関数が返却する値は無視されるが)
apply, callを用いて呼び出し
code:javascript
function f() {
this.result = arguments0 + arguments1 + arguments2
}
const obj1 = {}
const obj2 = {}
f.apply(obj1, 1, 2, 3)
f.call(obj2, 4, 5, 6)
console.log(obj1.result === 6) // true
console.log(obj2.result === 15) // true
apply, callは第1引数をfunction contextとして扱う。違いは第2引数の定義の仕方。
arrow functionとして呼び出し
code:javascript
const getThis = () => this
const obj = {
f: getThis
}
console.log(obj.f() === window) // true
arrow functionとして呼び出した場合、function contextは関数が定義された時点で固定される。
余談
arrow function内でargumentsを取ろうとしたらreference errorを起こした。使えないらしい。(それでいい気もするが)